/* vim: ft=c */
#ifndef VAR_INCLUDED
#define VAR_INCLUDED
/******************************************************************************
* (c)2025 Michael T. Richter
*
* This software is distributed under the terms of WTFPLv2.  The full terms and
* text of the license can be found at http://www.wtfpl.net/txt/copying
******************************************************************************/
/** @file
 *  @brief Simple var types in C.
 *
 */

/** @defgroup var Variant types in C.
 *
 * @{
 */

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#ifndef UNUSED
#define UNUSED(X) ((void)(X))
#endif

/** @brief Variant type tags. */
typedef enum var_tag_t
{
    VAR_INVALID = 0,
    VAR_BOOL,
    VAR_U8, VAR_U16, VAR_U32, VAR_U64,
    VAR_S8, VAR_S16, VAR_S32, VAR_S64,
    VAR_SIZE,
    VAR_FLOAT, VAR_DOUBLE,
} var_tag_t;

/** @brief Variant variable holder. */
typedef struct var_t
{
    var_tag_t tag;
    union
    {
        bool     BOOL;
        uint8_t  U8;
        uint16_t U16;
        uint32_t U32;
        uint64_t U64;
        int8_t   S8;
        int16_t  S16;
        int32_t  S32;
        int64_t  S64;
        size_t   SIZE;
        float    FLOAT;
        double   DOUBLE;
    };
} var_t;

/** @brief Error callback for var operational errors.
 *
 * @param [in] file          The source file where the error was discovered.
 * @param [in] line          The line of the file where the error was discovered.
 * @param [in] error_message The error's textual message.
 *
 * A function pointer to an error handler.  Assign your own function to the pointer to override.
 */
typedef void (*var_error_handler_t)(const char *file, const int line, const char *error_message);
extern var_error_handler_t var_error_handler;

/** @brief Convert a var_t to another var_tag_t.
 *
 * @param [in] value    A value to be converted.
 * @param [in] type     The type to convert to.
 *
 * Converts the provided value into a var_t of a new type.
 * Will call the error handler if information is lost.
 */
var_t var_convert(var_t value, var_tag_t type);

#ifdef VAR_IMPLEMENTATION

/*******************************************************************************
* Static helper function declarations
*******************************************************************************/
static var_tag_t get_broadest_type(var_t v1, var_t v2);
static inline size_t get_size(var_t v);

/*******************************************************************************
* API function definitions
*******************************************************************************/
var_t var_convert(var_t value, var_tag_t type)
{
    var_t rv = { 0 };

    if (value.tag == type)
    {
        return value;
    }
    else
    {
        switch (type)
        {

        }
    }

    return rv;
}

/*******************************************************************************
* Static helper function definitions
*******************************************************************************/
static var_tag_t get_broadest_type(var_t v1, var_t v2)
{
    var_tag_t rv = VAR_INVALID;

    // floating points are always broader than integers
    bool v1_fp = ((v1.tag == VAR_FLOAT) || (v1.tag == VAR_DOUBLE));
    bool v2_fp = ((v2.tag == VAR_FLOAT) || (v2.tag == VAR_DOUBLE));

    if (v1_fp)
    {
        if ((v1.tag == VAR_FLOAT) && (v2.tag == VAR_DOUBLE))
        {
            return v2.tag;
        }
    }
    else if (v2_fp)
    {
        if ((v2.tag == VAR_FLOAT) && (v1.tag == VAR_DOUBLE))
        {
            return v1.tag;
        }
    }

    // anything else we just use the size as given
}

static inline size_t get_size(var_t v)
{
    switch (v.tag)
    {
    case VAR_BOOL:   return sizeof(bool);
    case VAR_U8:     return sizeof(uint8_t);
    case VAR_U16:    return sizeof(uint16_t);
    case VAR_U32:    return sizeof(uint32_t);
    case VAR_U64:    return sizeof(uint64_t);
    case VAR_S8:     return sizeof(int8_t);
    case VAR_S16:    return sizeof(int16_t);
    case VAR_S32:    return sizeof(int32_t);
    case VAR_S64:    return sizeof(int64_t);
    case VAR_SIZE:   return sizeof(size_t);
    case VAR_FLOAT:  return sizeof(float);
    case VAR_DOUBLE: return sizeof(double);
    default:
        var_error_handler(__FILE__, __LINE__, "invalid type");
        return 0;
    }
}

void default_var_error_handler(const char *file, const int line, const char *error_message)
{
    UNUSED(file);
    UNUSED(line);
    UNUSED(error_message);
}
var_error_handler_t var_error_handler = default_var_error_handler;

#endif //VAR_IMPLEMENTATION

#endif //!VAR_INCLUDED
